using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using TallComponents.Web.Pdf.Resources;
using TallComponents.PDF.Navigation;

[assembly: WebResource(TallComponents.Web.Pdf.PdfBookmarksList.ScriptResourcePath, "text/javascript")]
[assembly: WebResource(TallComponents.Web.Pdf.PdfBookmarksList.ImageVerticalLinePath, "image/gif")]
[assembly: WebResource(TallComponents.Web.Pdf.PdfBookmarksList.ImageHorisontalLinePath, "image/gif")]
[assembly: WebResource(TallComponents.Web.Pdf.PdfBookmarksList.ImageCollapsedNodePath, "image/gif")]
[assembly: WebResource(TallComponents.Web.Pdf.PdfBookmarksList.ImageExpandedNodePath, "image/gif")]
[assembly: WebResource(TallComponents.Web.Pdf.PdfBookmarksList.BlankImagePath, "image/x-win-bitmap")]

namespace TallComponents.Web.Pdf
{
   /// <summary>
   /// Renders a list of thumbnails.
   /// </summary>
   [System.Drawing.ToolboxBitmap(typeof(PdfBookmarksList), IconPath)]
   [ToolboxItem("System.Web.UI.Design.WebControlToolboxItem, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
   public class PdfBookmarksList : ScriptControl
   {
      internal const string IconPath = "TallComponents.Web.Pdf.PdfWebViewer.ico";
      internal const string ImageVerticalLinePath = "TallComponents.Web.Pdf.Bookmarks.line1.gif";
      internal const string ImageHorisontalLinePath = "TallComponents.Web.Pdf.Bookmarks.line2.gif";
      internal const string ImageCollapsedNodePath = "TallComponents.Web.Pdf.Bookmarks.collapsed.gif";
      internal const string ImageExpandedNodePath = "TallComponents.Web.Pdf.Bookmarks.expanded.gif";
      internal const string BlankImagePath = "TallComponents.Web.Pdf.white.png";

      // This is the resource path to the javascript source file
      // Note that resource paths always start with the assembly default namespace
#if DEBUG
      internal const string ScriptResourcePath = "TallComponents.Web.Pdf.PdfBookmarksList.js";
#else
      internal const string ScriptResourcePath = "TallComponents.Web.Pdf.PdfBookmarksList.min.js";
#endif

      internal const string ClientControlType = "TallComponents.BookmarksList";

      /// <summary>
      /// Raises the <see cref="E:System.Web.UI.Control.PreRender"/> event.
      /// </summary>
      /// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
      protected override void OnPreRender(EventArgs e)
      {
         base.OnPreRender(e);

         if (string.IsNullOrEmpty(PdfViewerControlId))
            throw new InvalidOperationException(Text.PdfViewerControlId_not_set);

         var targetControl = FindTargetControl(PdfViewerControlId) as PdfViewer;

         if (null == targetControl)
            throw new InvalidOperationException(string.Format(Text.ViewerControl_not_found, PdfViewerControlId));

         _pageViewChildControlId = targetControl.ClientID;

         _storageKey = targetControl.StorageKey;
         _storageProviderName = targetControl.StorageProviderName;

         var listStyle = new CssStyle();
         listStyle.Add("margin", "0px");
         listStyle.Add("padding", "0px");
         listStyle.Add("list-style", "none");
         Page.Header.StyleSheet.CreateStyleRule(listStyle, this, string.Format("#{0}, #{0} ul, #{0} li", ClientID));

         var listBackgroundStyle = new CssStyle();
         string vLineUrl = HttpUtility.HtmlAttributeEncode(Page.ClientScript.GetWebResourceUrl(typeof(PdfThumbnailsList), ImageVerticalLinePath));
         listBackgroundStyle.Add("background", string.Format("url({0}) repeat-y", vLineUrl));
         Page.Header.StyleSheet.CreateStyleRule(listBackgroundStyle, this, string.Format("#{0} ul", ClientID));

         var listItemStyle = new CssStyle();
         listItemStyle.Add("line-height", "20px");
         listItemStyle.Add("margin-top", "1px");
         listItemStyle.Add("position", "relative");
         listItemStyle.Add("width", "100%");
         Page.Header.StyleSheet.CreateStyleRule(listItemStyle, this, string.Format("#{0} li", ClientID));

         //IE leaves a blank space where span is added so this is to avoid that
         var ieListItemStyle = new CssStyle();
         ieListItemStyle.Add("float", "left");
         ieListItemStyle.Add("display", "inline");
         Page.Header.StyleSheet.CreateStyleRule(ieListItemStyle, this, string.Format("* html #{0} li", ClientID));

         var listItemAStyle = new CssStyle();
         listItemAStyle.Add("padding-left", "28px");
         Page.Header.StyleSheet.CreateStyleRule(listItemAStyle, this, string.Format("#{0} li a", ClientID));

         var listItemSpanStyle = new CssStyle();
         listItemSpanStyle.Add("float", "left");
         listItemSpanStyle.Add("position", "absolute");
         listItemSpanStyle.Add("top", "5px");
         listItemSpanStyle.Add("left", "5px");
         listItemSpanStyle.Add("width", "13px");
         listItemSpanStyle.Add("height", "13px");
         listItemSpanStyle.Add("cursor", "auto");
         listItemSpanStyle.Add("font-size", "0");
         Page.Header.StyleSheet.CreateStyleRule(listItemSpanStyle, this, string.Format("#{0} li span", ClientID));

         var listItemCollapsedSpanStyle = new CssStyle();
         string collapsedNodeUrl = HttpUtility.HtmlAttributeEncode(Page.ClientScript.GetWebResourceUrl(typeof(PdfThumbnailsList), ImageCollapsedNodePath));
         listItemCollapsedSpanStyle.Add("background", string.Format("url({0}) no-repeat 0 0", collapsedNodeUrl));
         Page.Header.StyleSheet.CreateStyleRule(listItemCollapsedSpanStyle, this, string.Format("#{0} li span, #{0} li span.collapsed", ClientID));

         var listExpandedSpanItemStyle = new CssStyle();
         string expandedNodeUrl = HttpUtility.HtmlAttributeEncode(Page.ClientScript.GetWebResourceUrl(typeof(PdfThumbnailsList), ImageExpandedNodePath));
         listExpandedSpanItemStyle.Add("background", string.Format("url({0}) no-repeat 0 0", expandedNodeUrl));
         Page.Header.StyleSheet.CreateStyleRule(listExpandedSpanItemStyle, this, string.Format("#{0} li span.expanded", ClientID));

         var nestedListStyle = new CssStyle();
         nestedListStyle.Add(" margin-left", "28px");
         nestedListStyle.Add("background", string.Format("url({0}) repeat-y", vLineUrl));
         Page.Header.StyleSheet.CreateStyleRule(nestedListStyle, this, string.Format("#{0} li ul", ClientID));

         var nestedListItemStyle = new CssStyle();
         string hLineUrl = HttpUtility.HtmlAttributeEncode(Page.ClientScript.GetWebResourceUrl(typeof(PdfThumbnailsList), ImageHorisontalLinePath));
         nestedListItemStyle.Add("background", string.Format("url({0}) no-repeat 0 0", hLineUrl));
         Page.Header.StyleSheet.CreateStyleRule(nestedListItemStyle, this, string.Format("#{0} li li", ClientID));

         var listAfterStyle = new CssStyle();
         listAfterStyle.Add("content:", "\".\"");
         listAfterStyle.Add("display", "block");
         listAfterStyle.Add("height", "0");
         listAfterStyle.Add("clear", "both");
         listAfterStyle.Add("visibility", "hidden");
         Page.Header.StyleSheet.CreateStyleRule(listAfterStyle, this, string.Format("#{0}:after, #{0} ul:after", ClientID));

         var nestedList2Style = new CssStyle();
         nestedList2Style.Add("display", "block");
         nestedList2Style.Add("min-height", "1%");
         Page.Header.StyleSheet.CreateStyleRule(nestedList2Style, this, string.Format("#{0}, #{0} ul", ClientID));

         var ieNestedListStyle = new CssStyle();
         ieNestedListStyle.Add("height", "1%");
         Page.Header.StyleSheet.CreateStyleRule(ieNestedListStyle, this, string.Format("* html #{0}, * html #{0} ul", ClientID));

         //var nestedListStyle2 = new CssStyle();
         //nestedListStyle2.Add("display", "none");
         //Page.Header.StyleSheet.CreateStyleRule(nestedListStyle2, this, string.Format("#{0} ul", ClientID));
      }

      /// <summary>
      /// Renders the control to the specified HTML writer.
      /// </summary>
      /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the control content.</param>
      protected override void Render(HtmlTextWriter writer)
      {
         AddAttributesToRender(writer);
         writer.AddStyleAttribute(HtmlTextWriterStyle.Overflow, "auto");
         writer.AddStyleAttribute(HtmlTextWriterStyle.Position, "relative");
         writer.AddStyleAttribute(HtmlTextWriterStyle.OverflowX, "hidden");
         writer.AddStyleAttribute(HtmlTextWriterStyle.OverflowY, "scroll");
         writer.RenderBeginTag(HtmlTextWriterTag.Div);

         string url = HttpUtility.HtmlAttributeEncode(Page.ClientScript.GetWebResourceUrl(typeof(PdfThumbnailsList), BlankImagePath));
         writer.AddAttribute(HtmlTextWriterAttribute.Src, url);
         writer.AddStyleAttribute(HtmlTextWriterStyle.Width, "100%");
         writer.AddStyleAttribute(HtmlTextWriterStyle.Height, "100%");
         writer.AddStyleAttribute(HtmlTextWriterStyle.Visibility, "hidden");
         writer.RenderBeginTag(HtmlTextWriterTag.Img);
         writer.RenderEndTag();

         writer.AddStyleAttribute(HtmlTextWriterStyle.Overflow, "hidden");
         writer.AddStyleAttribute(HtmlTextWriterStyle.Position, "absolute");
         writer.AddStyleAttribute(HtmlTextWriterStyle.Left, "0px");
         writer.AddStyleAttribute(HtmlTextWriterStyle.Top, "0px");
         writer.RenderBeginTag(HtmlTextWriterTag.Div);

         writer.RenderBeginTag(HtmlTextWriterTag.Ul);

         if (!DesignMode)
         {
            if (!string.IsNullOrEmpty(_storageKey))
            {
               Document doc = Storage.GetDocument(_storageKey);
               if (null != doc)
               {

                  foreach (var bookmark in doc.Bookmarks)
                  {
                     RenderBookmark(bookmark, writer, IsExpanded, doc);
                  }
               }
            }
         }

         writer.RenderEndTag(); // div
         writer.RenderEndTag(); // ul
         writer.RenderEndTag(); // div

         if (!DesignMode)
         {
            ScriptManager.RegisterScriptDescriptors(this);
         }
      }

      private void RenderBookmark(Bookmark bookmark, HtmlTextWriter writer, bool isExpanded, Document doc)
      {
         writer.RenderBeginTag(HtmlTextWriterTag.Li);
         if (null != _bookmarksStyle)
            _bookmarksStyle.AddAttributesToRender(writer);

         var visitor = new TallComponents.Web.Pdf.Bookmarks.BookmarkActionVisitor(doc);
         bookmark.Actions.Accept(visitor, null);
         //visitor should return Page, Zoom, Position
         //create a '<a>' element, create an ID for it and put together with Page, Zoom, Position
         //and put all these info to the __bookmarkDataList
         //var actionScript = visitor.Javascript;
         var bookmarkAction = visitor.BookmarkAction;
         bookmarkAction.AId = String.Format("bkmrkiddd{0}", _linkId++);
         _bookmarkActions.Add(bookmarkAction);

         writer.AddAttribute(HtmlTextWriterAttribute.Href, "#");
         writer.AddAttribute(HtmlTextWriterAttribute.Id, bookmarkAction.AId);

         writer.RenderBeginTag(HtmlTextWriterTag.A);
         writer.Write(bookmark.Title);
         writer.RenderEndTag(); //a

         string ulId = null;

         if (bookmark.Bookmarks.Count > 0)
         {
            ulId = Guid.NewGuid().ToString().Replace("-", "").Insert(0, "i");

            string script = "var ul = getElementById(\"" + ulId + "\");\n" +
                               "ul.style.display = (ul.style.display == \"none\") ? \"block\" : \"none\";\n" +
                               "this.className = (ul.style.display == \"none\") ? \"collapsed\" : \"expanded\";";

            writer.AddAttribute(HtmlTextWriterAttribute.Onclick, script);
            writer.AddAttribute(HtmlTextWriterAttribute.Class, (isExpanded) ? "expanded" : "collapsed");
            writer.RenderBeginTag(HtmlTextWriterTag.Span);
            writer.RenderEndTag(); //span
         }

         if (bookmark.Bookmarks.Count > 0)
         {
            writer.AddStyleAttribute(HtmlTextWriterStyle.Display, (isExpanded) ? "block" : "none");
            writer.AddAttribute(HtmlTextWriterAttribute.Id, ulId);
            writer.RenderBeginTag(HtmlTextWriterTag.Ul);
            foreach (var bmrk in bookmark.Bookmarks)
            {
               RenderBookmark(bmrk, writer, isExpanded, doc);
            }
            writer.RenderEndTag();
         }

         writer.RenderEndTag(); //li
      }

      /// <summary>
      /// Searches the control hierarchy to locate the named control.
      /// </summary>
      /// <param name="id">The control id of the control to look for.</param>
      /// <returns>The control with the specified <paramref name="id"/> or <c>null</c> if no matching control is found.</returns>
      /// /// <exception cref="ArgumentException">Occures when <paramref name="id"/> is <c>null</c> or empty.</exception>
      protected Control FindTargetControl(string id)
      {
         if (string.IsNullOrEmpty("id"))
            throw new ArgumentException("id");

         Control ctrl = base.FindControl(id);
         Control namingContainer = NamingContainer;

         // Walk up into the control tree to locate the target control
         while ((null == ctrl) && (null != namingContainer))
         {
            ctrl = namingContainer.FindControl(id);
            namingContainer = namingContainer.NamingContainer;
         }

         return ctrl;
      }

      /// <summary>
      /// Gets or sets the page view control id.
      /// </summary>
      /// <value>The page view control id.</value>
      [IDReferenceProperty(typeof(PdfViewer))]
      public string PdfViewerControlId
      {
         get { return _pageViewControlId; }
         set { _pageViewControlId = value; }
      }

      /// <summary>
      /// Gets the bookmarks style.
      /// </summary>
      /// <value>The bookmarks style.</value>
      private Style BookmarksStyle
      {
         get
         {
            if (null == _bookmarksStyle)
               _bookmarksStyle = new Style();
            return _bookmarksStyle;
         }
      }

      /// <summary>
      /// Gets or sets a value indicating whether the list of the bookmarks is expanded.
      /// </summary>
      /// <value>
      /// 	<c>true</c> if the list of the bookmarks is expanded; otherwise, <c>false</c>.
      /// </value>
      public bool IsExpanded { get; set; }

      /// <summary>
      /// Gets or sets the CSS class applied to all bookmarks.
      /// </summary>
      /// <value>The bookmark CSS class.</value>
      public string BookmarkCssClass
      {
         get { return BookmarksStyle.CssClass; }
         set { BookmarksStyle.CssClass = value; }
      }

      /// <summary>
      /// Gets or sets the CSS class for the thumbnail that corresponds to the active page
      /// in the associated PdfViewer.
      /// </summary>
      /// <value>The thumbnail active CSS class.</value>
      public string SelectedBookmarkCssClass { get; set; }

      /// <summary>
      /// Gets the script manager for the current request.
      /// </summary>
      /// <value>The script manager for the current request.</value>
      private ScriptManager ScriptManager
      {
         get
         {
            if (_scriptManager == null)
            {
               if (Page == null)
               {
                  throw new InvalidOperationException(Text.Common_PageCannotBeNull);
               }
               _scriptManager = ScriptManager.GetCurrent(Page);
               if (_scriptManager == null)
               {
                  throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, Text.Common_ScriptManagerRequired, ID));
               }
            }
            return _scriptManager;
         }
      }

      /// <summary>
      /// Gets the PDF storage.
      /// </summary>
      /// <value>The PDF storage.</value>
      private TallComponents.Web.Storage.IPdfStorage Storage
      {
         get
         {
            if (_storage == null)
            {
               if (_storageProviderName == null)
                  _storage = TallComponents.Web.Storage.PdfStorage.Provider;
               else
                  _storage = TallComponents.Web.Storage.PdfStorage.Providers[_storageProviderName];
            }
            return _storage;
         }
      }

      /// <summary>
      /// Returns the <see cref="T:System.Web.UI.ScriptDescriptor"/> objects for the control.
      /// </summary>
      /// <returns>
      /// An <see cref="T:System.Collections.IEnumerable"/> collection of <see cref="T:System.Web.UI.ScriptDescriptor"/> objects.
      /// </returns>
      protected override IEnumerable<ScriptDescriptor> GetScriptDescriptors()
      {
         var desc = new ScriptControlDescriptor(ClientControlType, ClientID);
         desc.AddProperty("targetControlId", _pageViewChildControlId);
         if (!string.IsNullOrEmpty(BookmarkCssClass))
            desc.AddProperty("activeCssClass", SelectedBookmarkCssClass);
         desc.AddProperty("storageProviderName", _storageProviderName);
         desc.AddProperty("bookmarkDataList", _bookmarkActions);
         yield return desc;
      }

      /// <summary>
      /// Returns the script files for the control.
      /// </summary>
      /// <returns>
      /// An <see cref="T:System.Collections.IEnumerable"/> collection that contains ECMAScript (JavaScript) files that have been registered as embedded resources.
      /// </returns>
      protected override IEnumerable<ScriptReference> GetScriptReferences()
      {
         var scriptReferences = new List<ScriptReference> {
            new ScriptReference( ScriptResourcePath, "TallComponents.Web.PdfViewer" )
         };
         return scriptReferences;
      }

      private TallComponents.Web.Storage.IPdfStorage _storage;
      private ScriptManager _scriptManager;

      Style _bookmarksStyle;
      string _pageViewControlId;
      string _pageViewChildControlId;
      string _storageKey;
      int _linkId = 1;
      private string _storageProviderName;

      List<BookmarkAction> _bookmarkActions = new List<BookmarkAction>();
   }

   internal class BookmarkAction
   {
      public string AId;
      public string Page;
      public string Zoom;
      public double PosX;
      public double PosY;
      public string Url;
      public string Target;
   }
}
